﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Storage;
using Windows.Storage.Pickers;
using Windows.UI.Popups;
using wBudget.Common;
using wBudget.Models;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using Windows.UI.ApplicationSettings;
using Windows.System;
using Windows.UI.Xaml.Media.Imaging;
using System.Threading.Tasks;
using Windows.Storage.Streams;

// The Split Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234234

namespace wBudget
{
    /// <summary>
    /// A page that displays a group title, a list of items within the group, and details for
    /// the currently selected item.
    /// </summary>
    public sealed partial class MainPage
    {
        private ObservableCollection<Entry> currentEntries;
        private ObservableCollection<Category> currentCategories; 
        private readonly Brush defaultBrush;
        private StorageFile imageFile;
        private string oldActionTitleText;
        private IList<object> oldSelectedCategories;

        public MainPage()
        {
            InitializeComponent();
            DataManager.Init();
            // Hide new Category Form
            NewCategoryForm.Visibility = Visibility.Collapsed;
            NewEntryForm.Visibility = Visibility.Collapsed;
            ItemDetailGrid.Visibility = Visibility.Collapsed;

            currentCategories = new ObservableCollection<Category>(DataManager.Categories);
            CategoriesViewSource.Source = currentCategories;
            currentEntries = new ObservableCollection<Entry>(DataManager.Entries);

            EntriesViewSource.Source = currentEntries;
            defaultBrush = SumField.Foreground;
            CalculateSum();
            ItemDetailGrid.Visibility = Visibility.Visible;
            ApplicationSettings.ApplicationSettingsChanged += CalculateSum;
        }

        #region Page state management

        /// <summary>
        /// Populates the page with content passed during navigation.  Any saved state is also
        /// provided when recreating a page from a prior session.
        /// </summary>
        /// <param name="navigationParameter">The parameter value passed to
        /// <see cref="Frame.Navigate(Type, Object)"/> when this page was initially requested.
        /// </param>
        /// <param name="pageState">A dictionary of state preserved by this page during an earlier
        /// session.  This will be null the first time a page is visited.</param>
        protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
        {
            // TODO: Assign a bindable group to this.DefaultViewModel["Group"]
            // TODO: Assign a collection of bindable items to this.DefaultViewModel["Items"]

            if (pageState == null)
            {
                // When this is a new page, select the first item automatically unless logical page
                // navigation is being used (see the logical page navigation #region below.)
                if (!UsingLogicalPageNavigation() && CategoriesViewSource.View != null)
                {
                    CategoriesViewSource.View.MoveCurrentToFirst();
                }
            }
            else
            {
                // Restore the previously saved state associated with this page
                if (pageState.ContainsKey("SelectedItem") && CategoriesViewSource.View != null)
                {
                    // TODO: Invoke this.itemsViewSource.View.MoveCurrentTo() with the selected
                    //       item as specified by the value of pageState["SelectedItem"]
                }
            }
        }

        /// <summary>
        /// Preserves state associated with this page in case the application is suspended or the
        /// page is discarded from the navigation cache.  Values must conform to the serialization
        /// requirements of <see cref="SuspensionManager.SessionState"/>.
        /// </summary>
        /// <param name="pageState">An empty dictionary to be populated with serializable state.</param>
        protected override void SaveState(Dictionary<String, Object> pageState)
        {
            if (CategoriesViewSource.View != null)
            {
                var selectedItem = CategoriesViewSource.View.CurrentItem;
                // TODO: Derive a serializable navigation parameter and assign it to
                //       pageState["SelectedItem"]
            }
        }

        #endregion

        #region page calculation helper functions

        public void CalculateSum()
        {
            double result = 0;
            foreach (Entry ce in currentEntries)
            {
                result += ce.Amount;
            }
            result = Math.Round(result, 2);
            SumField.Text = result.ToString("0.00");
            if (DataManager.UseCustomCurrency) SumField.Text += " " + DataManager.CurrencyString;
            if (result < 0)
            {
                SumField.Foreground = new SolidColorBrush(Windows.UI.Colors.Red);
            }
            else if (result > 0)
            {
                SumField.Foreground = new SolidColorBrush(Windows.UI.Colors.Green);
            }
            else
            {
                SumField.Foreground = defaultBrush;
            }
        }

        #endregion

        #region Logical page navigation

        // Visual state management typically reflects the four application view states directly
        // (full screen landscape and portrait plus snapped and filled views.)  The split page is
        // designed so that the snapped and portrait view states each have two distinct sub-states:
        // either the item list or the details are displayed, but not both at the same time.
        //
        // This is all implemented with a single physical page that can represent two logical
        // pages.  The code below achieves this goal without making the user aware of the
        // distinction.

        /// <summary>
        /// Invoked to determine whether the page should act as one logical page or two.
        /// </summary>
        /// <param name="viewState">The view state for which the question is being posed, or null
        /// for the current view state.  This parameter is optional with null as the default
        /// value.</param>
        /// <returns>True when the view state in question is portrait or snapped, false
        /// otherwise.</returns>
        private bool UsingLogicalPageNavigation(ApplicationViewState? viewState = null)
        {
            if (viewState == null) viewState = ApplicationView.Value;
            return viewState == ApplicationViewState.FullScreenPortrait ||
                viewState == ApplicationViewState.Snapped;
        }

        /// <summary>
        /// Invoked when an item within the list is selected.
        /// </summary>
        /// <param name="sender">The GridView (or ListView when the application is Snapped)
        /// displaying the selected item.</param>
        /// <param name="e">Event data that describes how the selection was changed.</param>
        void ItemListViewSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            // Invalidate the view state when logical page navigation is in effect, as a change
            // in selection may cause a corresponding change in the current logical page.  When
            // an item is selected this has the effect of changing from displaying the item list
            // to showing the selected item's details.  When the selection is cleared this has the
            // opposite effect.
            if (UsingLogicalPageNavigation()) InvalidateVisualState();
            var list = sender as ListView;
            EntriesViewSource.Source = null;
            if (list != null)
            {
                currentEntries = new ObservableCollection<Entry>(
                    DataManager.GetEntriesForCategories(list.SelectedItems.Cast<Category>().ToList())
                    );
            }
            EntriesViewSource.Source = currentEntries;
            CalculateSum();
        }

        /// <summary>
        /// Invoked when the page's back button is pressed.
        /// </summary>
        /// <param name="sender">The back button instance.</param>
        /// <param name="e">Event data that describes how the back button was clicked.</param>
        protected override void GoBack(object sender, RoutedEventArgs e)
        {
            if (UsingLogicalPageNavigation() && ItemListView.SelectedItem != null)
            {
                // When logical page navigation is in effect and there's a selected item that
                // item's details are currently displayed.  Clearing the selection will return to
                // the item list.  From the user's point of view this is a logical backward
                // navigation.
                ItemListView.SelectedItem = null;
            }
            else
            {
                // When logical page navigation is not in effect, or when there is no selected
                // item, use the default back button behavior.
                base.GoBack(sender, e);
            }
        }

        /// <summary>
        /// Invoked to determine the name of the visual state that corresponds to an application
        /// view state.
        /// </summary>
        /// <param name="viewState">The view state for which the question is being posed.</param>
        /// <returns>The name of the desired visual state.  This is the same as the name of the
        /// view state except when there is a selected item in portrait and snapped views where
        /// this additional logical page is represented by adding a suffix of Detail.</returns>
        protected override string DetermineVisualState(ApplicationViewState viewState)
        {
            // Update the back button's enabled state when the view state changes
            var logicalPageBack = UsingLogicalPageNavigation(viewState) && ItemListView.SelectedItem != null;
            var physicalPageBack = Frame != null && Frame.CanGoBack;
            DefaultViewModel["CanGoBack"] = logicalPageBack || physicalPageBack;

            // Determine visual states for landscape layouts based not on the view state, but
            // on the width of the window.  This page has one layout that is appropriate for
            // 1366 virtual pixels or wider, and another for narrower displays or when a snapped
            // application reduces the horizontal space available to less than 1366.
            if (viewState == ApplicationViewState.Filled ||
                viewState == ApplicationViewState.FullScreenLandscape)
            {
                var windowWidth = Window.Current.Bounds.Width;
                if (windowWidth >= 1366) return "FullScreenLandscapeOrWide";
                return "FilledOrNarrow";
            }

            // When in portrait or snapped start with the default visual state name, then add a
            // suffix when viewing details instead of the list
            var defaultStateName = base.DetermineVisualState(viewState);
            return logicalPageBack ? defaultStateName + "Detail" : defaultStateName;
        }

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            SettingsPane.GetForCurrentView().CommandsRequested += MainPageCommandsRequested;
        }

        void MainPageCommandsRequested(SettingsPane sender, SettingsPaneCommandsRequestedEventArgs args)
        {
            var settingsCmd = new SettingsCommand("setCmd", "Settings", c => ApplicationSettings.ShowSettings());

            args.Request.ApplicationCommands.Add(settingsCmd);


            var privacyCmd = new SettingsCommand("privacyStatementCommand", "Privacy Statement", async x =>
            {
                MessageDialog md = new MessageDialog("", "Privacy Statement");
                md.Content = "wBudget saves your data on your local machine in a file based database.\nHowever, your data is shared between all your Windows 8-devices using the \"Roaming Folder\".\nUsing this feature your data is linked to your Microsoft-account and can not be accessed by others.\nwBudget does not share your data with anyone else than you - nothing is stored on any server(s).\nFor more information about the \"Roaming Folder\" technology visit: http://blogs.msdn.com/b/windowsappdev/archive/2012/07/17/roaming-your-app-data.aspx";
                await md.ShowAsync();
            });

            args.Request.ApplicationCommands.Add(privacyCmd);
        }

        protected override void OnNavigatedFrom(NavigationEventArgs e)
        {
            SettingsPane.GetForCurrentView().CommandsRequested -= MainPageCommandsRequested;
        }

        #endregion

        private void addCategoryButtonClick(object sender, RoutedEventArgs e)
        {
            oldSelectedCategories = ItemListView.SelectedItems;
            ItemListView.SelectionMode = ListViewSelectionMode.None;
            ItemListView.SelectedItem = null;
            ItemListView.IsEnabled = false;
            ItemDetailGrid.Visibility = Visibility.Collapsed;
            NewEntryForm.Visibility = Visibility.Collapsed;
            oldActionTitleText = ActionTitle.Text;
            ActionTitle.Text = "New Category";
            
            NewCategoryForm.Visibility = Visibility.Visible;
        }

        private void restoreCategoriesSelection()
        {
            ItemListView.IsEnabled = true;
            ItemListView.SelectionMode = ListViewSelectionMode.Multiple;
            foreach (object o in oldSelectedCategories)
            {
                ItemListView.SelectedItems.Add(o);
            }
        }

        private async void SelectImage(object sender, RoutedEventArgs e)
        {

            var picker = new FileOpenPicker { SuggestedStartLocation = PickerLocationId.PicturesLibrary };
            picker.FileTypeFilter.Add(".jpg");
            picker.FileTypeFilter.Add(".png");
            picker.FileTypeFilter.Add(".jpeg");
            picker.FileTypeFilter.Add(".bmp");
            picker.ViewMode = PickerViewMode.List;
            imageFile = await picker.PickSingleFileAsync();
            
            if(imageFile != null)
                selectedImage.Source = await LoadSingleImage(imageFile);
        }

        private async Task<BitmapImage> LoadSingleImage(StorageFile image)
        {
            FileRandomAccessStream stream = (FileRandomAccessStream)await image.OpenAsync(FileAccessMode.Read);
            BitmapImage bi = new BitmapImage();
            bi.SetSource(stream);
            return bi;
        }

        private async void AddCategory(object sender, RoutedEventArgs e)
        {
            string categoryName = CategoryName.Text.Trim();
            if (imageFile != null && categoryName != String.Empty)
            {
                var targetFile = await ApplicationData.Current.RoamingFolder.CreateFileAsync(DateTime.Now.Ticks + imageFile.FileType);
                if (targetFile != null)
                {
                    await imageFile.CopyAndReplaceAsync(targetFile);
                    var category = new Category
                        {
                            Id = DataManager.GetHighestCategoryId() + 1,
                            Title = categoryName,
                            BImage = targetFile.Path
                        };
                    DataManager.Add(category);
                    currentCategories.Add(category);
                    ItemDetailGrid.Visibility = Visibility.Visible;
                    oldActionTitleText = ActionTitle.Text;
                    ActionTitle.Text = "Details";
                    restoreCategoriesSelection();
                    NewCategoryForm.Visibility = Visibility.Collapsed;
                }
            }

        }

        private void addEntryButtonClick(object sender, RoutedEventArgs e)
        {
            ItemListView.SelectionMode = ListViewSelectionMode.Single;
            ItemListView.SelectedItem = null;
            ItemDetailGrid.Visibility = Visibility.Collapsed;
            NewCategoryForm.Visibility = Visibility.Collapsed;
            oldActionTitleText = ActionTitle.Text;
            ActionTitle.Text = "New Entry";
            NewEntryForm.Visibility = Visibility.Visible;
            List<String> source = new List<String>();
            source.Add("Outgoing");
            source.Add("Incoming");
            EntryType.ItemsSource = source;
            EntryType.SelectedIndex = 0;
        }

        private bool parseDouble(string s, out double d)
        {
            if (s.Contains(","))
            {
                d = 0;
                return false;
            }
            return double.TryParse(s, out d);
        }

        private void AddEntry(object sender, RoutedEventArgs e)
        {
            Entry newEntry = new Entry();
            double amount = 0;
            double pricePerPiece = 0;
            bool success = true;
            if (EntryAmount.Text.Equals(""))
            {
                amount = 1;
            }
            else
            {
                success = parseDouble(EntryAmount.Text, out amount);   
            }
            if (!success)
            {
                ErrorMessage.Show("Number conversion failed", '"' + EntryAmount.Text + '"' + " is not a valid number!");
            }
            else
            {
                success = success && parseDouble(CostPerPiece.Text, out pricePerPiece);
                if (EntryDescription.Text.Equals(""))
                {
                    success = false;
                    ErrorMessage.Show("No description given", "The new entry must have a description!");
                }
                if (success && ItemListView.SelectedItem == null)
                {
                    success = false;
                    ErrorMessage.Show("No category given", "You have to select a category from the left!");
                }
                if (success)
                {
                    int sign = EntryType.SelectedItem.Equals("Outgoing") ? -1 : 1;
                    newEntry.Amount = (amount*pricePerPiece)*sign;
                    string prefix = !amount.Equals(1) ? amount.ToString() + " * " : "";
                    newEntry.Description = prefix + EntryDescription.Text;
                    Category cat = (from c in currentCategories
                                    where c.Equals(ItemListView.SelectedValue)
                                    select c).Single();
                    newEntry.CategoryId = cat.Id;
                    newEntry.Date = DateTime.Now;
                    DataManager.Add(newEntry);
                    NewEntryForm.Visibility = Visibility.Collapsed;
                    restoreCategoriesSelection();
                    ItemDetailGrid.Visibility = Visibility.Visible;
                    currentEntries.Add(newEntry);
                    CalculateSum();
                }
                else
                {
                    ErrorMessage.Show("Number conversion failed", '"' + CostPerPiece.Text + '"' + " is not a valid number!");
                }
            }
        }

        private void CancelClick(object sender, RoutedEventArgs e)
        {
            NewCategoryForm.Visibility = Visibility.Collapsed;
            NewEntryForm.Visibility = Visibility.Collapsed;
            ItemDetailGrid.Visibility = Visibility.Collapsed;
            ActionTitle.Text = oldActionTitleText;
            restoreCategoriesSelection();
            ItemDetailGrid.Visibility = Visibility.Visible;
        }
    }
}
